﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

深入理解指针5

一；sizeof和strlen对比
1.1.sizeof是操作符
在操作符的时候学习的sizeof，sizeof计算变量所占内存空间大小，单位是字节。如果操作数是类型
计算的是使用该类型创建变量所占内存空间的大小
sizeof只关注占用内存空间的大小，不在乎内存中存放什么数据类型

1.2.strlen是库函数，头文件<string,h>
size_t strlen(const char*str),strlen函数的参数和返回类型
strlen；只求字符串的长度，只针对字符串（字符数组），统计的是字符串中\0之前的字符个数，从strlen函数
的参数str中地址开始向后统计，遇到\0停止，如果没有则会一直统计，有可能越界访问


二；数组和指针笔试题分析

一维数组；
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%zd\n", sizeof(a));//16   a单独存在，所以这里计算的是整个数组的大小，单位是字节
	printf("%zd\n", sizeof(a + 0));//4/8   //这里的a是数组名表示首元素的地址，a+0还是首元素的地址
	//这里sizeof计算的是首元素地址的大小 4/8
	printf("%zd\n", sizeof(*a));//4   这里的a是数组名表示首元素的地址,*a 就是首元素，就是a[0]
	//*a --*(a+0)-- a[0]
	printf("%zd\n", sizeof(a + 1));//4/8   //这里的a是数组名表示首元素的地址,a+1是第二个元素的地址(&a[1]
	//计算的是地址的大小 4/8
	printf("%zd\n", sizeof(a[1]));//4   a[1]表示第二个元素
	printf("%zd\n", sizeof(&a));//4/8   &a表示整个数组的地址，数组的地址也是地址
	printf("%zd\n", sizeof(*&a));//16   &a是取整个数组地址，在*&a就是数组a，所以就是计算数组a的大小
	printf("%zd\n", sizeof(&a + 1));//4/8    &a就是整个数组的地址，&a+1就是数组a后面的地址，也还是地址
	printf("%zd\n",sizeof(&a[0]));//4/8   a[0]是首元素，&a[0]就是首元素地址，还是地址
	printf("%zd\n", sizeof(&a[0]+1));//4/8    a[0]是首元素，&a[0]就是首元素地址，&a[0]+1就是第二个元素地址

	return 0;
}


字符数组；
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%zd\n", sizeof(arr));//6   arr单独存放表示整个数组，所以求整个数组的大小
	printf("%zd\n", sizeof(arr + 0));//4/8   arr不是单独存放，arr表示首元素地址，arr+0还是首元素地址
	printf("%zd\n", sizeof(*arr));//1   arr表示首元素地址，*arr就是首元素
	printf("%zd\n", sizeof(arr[1]));//1   arr[1]表示第二个元素
	printf("%zd\n", sizeof(&arr));//4/8   arr表示首元素地址，&arr还是地址
	printf("%zd\n", sizeof(&arr + 1));//4/8   arr表示首元素地址，&arr+1还是地址
	printf("%zd\n", sizeof(&arr[0] + 1));//4/8   &arr[0]首元素地址，所以&arr[0]+1就是第二个元素地址

	return 0;
}


int main()
{

	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));//随机值   arr表示首元素地址，但数组元素不是字符串，没有\0
	//所以会一直统计到有\0，可能越界访问
	printf("%d\n", strlen(arr + 0));//随机值 arr+0表示首元素地址，但数组元素不是字符串，没有\0
	//所以会一直统计到有\0，可能越界访问
	//printf("%d\n", strlen(*arr));//arr是数组名表示首元素的地址
	//*arr 是首元素-'a’-97传递给strlen后，strlen 会认为97就是地址，然后去访问内存
	//err -- 程序奔溃
	//printf("%d\n", strlen(arr[1]));//arr[1]表示"b"，所以还是报错
	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//随机值

	return 0;
}


int main()
{

	char arr[] = "abcdef";
	printf("%zd\n", sizeof(arr));//7   arr表示整个数组，求整个数组大小，这是字符串含有\0，所以是7字节
	printf("%zd\n", sizeof(arr + 0));//4/8   arr不是单独存放就表示首元素地址，所以arr+0还是首元素地址
	printf("%zd\n", sizeof(*arr));//1   arr不是单独存放表示首元素地址，*arr表示字符”a“
	printf("%zd\n", sizeof(arr[1]));//1   arr[1]表示第二元素
	printf("%zd\n", sizeof(&arr));//4/8   arr单独存放表示首元素地址，&arr还是地址
	printf("%zd\n", sizeof(&arr + 1));//4/8   arr单独存放表示首元素地址，&arr+1还是地址
	printf("%zd\n", sizeof(&arr[0] + 1));//4/8   &arr[0]首元素地址，&arr[0]+1第二个元素地址，还是地址

	return 0;
}


int main()
{

	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//6   arr是首元素地址，因为元素是字符串有\0，所以统计到f就停止了
	printf("%d\n", strlen(arr + 0));//6   arr+0还是首元素地址
	//printf("%d\n", strlen(*arr));//报错   *arr表示字符"a'->97,strlen(97)，97地址不能被访问，所以会报错
	//printf("%d\n", strlen(arr[1]));//报错   arr[1]—>"b"
	printf("%d\n", strlen(&arr));//6   &arr是整个数组的地址，但数组的地址和数组首元素的地址是指向同一个位置的
	//strlen还是从第一个元素开始统计
	printf("%d\n", strlen(&arr + 1));//随机值  &arr是整个数组地址，&arr+1就到数组后面了
	printf("%d\n", strlen(&arr[0] + 1));//5   &arr[0]+1第二个元素地址

	return 0;
}


int main()
{

	char* p = "abcdef";
	printf("%zd\n", sizeof(p));//4/8   字符指针，所以p表示首元素地址
	printf("%zd\n", sizeof(p + 1));//4/8   p+1表示第二个元素地址
	printf("%zd\n", sizeof(*p));//1   *p表示字符"a"
	printf("%zd\n", sizeof(p[0]));//1   p[0]->*(p+0)
	printf("%zd\n", sizeof(&p));//4/8
	printf("%zd\n", sizeof(&p + 1));//4/8
	printf("%zd\n", sizeof(&p[0] + 1));//4/8   p[0]->*p,&*p->p

	return 0;
}

int main()
{

	char* p = "abcdef";
	printf("%d\n", strlen(p));//6
	printf("%d\n", strlen(p + 1));//5
	//printf("%d\n", strlen(*p));//报错
	//printf("%d\n", strlen(p[0]));//报错  p[0]->*(p+0)！！！
	printf("%d\n", strlen(&p));//随机值
	printf("%d\n", strlen(&p + 1));//随机值
	printf("%d\n", strlen(&p[0] + 1));//5

	return 0;
}



二维数组；
提示二维数组假设a[3][4],它就是三个一维数组的数组，这三个一维数组名是a[0],a[1],a[2]
int main()
{

	int a[3][4] = { 0 };
	printf("%zd\n", sizeof(a));//48   a单独存放所以表示整个数组，类型整型，单位字节
	printf("%zd\n", sizeof(a[0][0]));//4   a[0][0]就是第一行第一个元素，类型整型所以大小是4个字节
	printf("%zd\n", sizeof(a[0]));//16   a[0]单独存放的所有表示第一行的元素地址（所有元素）
	printf("%zd\n", sizeof(a[0] + 1));//4/8   a[0]不是单独存放，所以表示第一行的数组名和第一行第一个元素地址
	//a[0]+1表示第一行第二个元素地址
	printf("%zd\n", sizeof(*(a[0] + 1)));//4   a[0]+1表示第一行第二个元素地址，在*解引用就是第二个元素
	printf("%zd\n", sizeof(a + 1));//4/8   a不是单独存放就表示第一行的地址，a+1表示第二行的地址
	printf("%zd\n", sizeof(*(a + 1)));//16   a不是单独存放就表示第一行的地址，a+1表示第二行的地址
	//*（a+1)表示第二行的元素
	printf("%zd\n", sizeof(&a[0] + 1));//4/8    &a[0]表示第一行的地址，加1表示第二行地址
	printf("%zd\n", sizeof(*(&a[0] + 1)));//16
	printf("%zd\n", sizeof(*a));//16
	printf("%zd\n", sizeof(a[3]));//16
	//sizeof内部的表达式是不会真实计算的
	//a[3]-第四行的数组名 int[4]

	return 0;
}



指针运算笔试题解析

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* pa = (int*)(&arr + 1);//&arr+1指向数组后面，所以pa也指向数组后面且是int*类型
	printf("%d %d ", *(arr + 1), *(pa - 1));
	//2 5
	return 0;
}






在X86环境下 
假设结构体的⼤⼩是20个字节 
程序输出的结果是啥？ 
struct Test
{
	
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;    //p这是结构体指针变量，它是结构体指针类型
//Ox100000是十六进制的数，所以（struct Test*）这里强制类型转换
//p现在就是Ox100000
int main()
{
	printf("%p\n", p + 0x1);//Ox1就是1，这里p+1相当于地址加1，要判断指针类型
	//这里是结构体指针变量，并且它的大小是20个字节，所以+1—>+1*20！！！
	//由于p存储的是16进制的数，所以20转换成十六进制14，所以p = Ox100014
	printf("%p\n", (unsigned long)p + 0x1);//这里把p强制类型转换为无符号长整型，所以就是整型的加减直接加1
	//即Ox100000 + 1 = Ox10001 
	printf("%p\n", (unsigned int*)p + 0x1);//这里把p强制类型转换为无符号整型指针变量，无符号整型大小是4个字节
	//所以+1—>+1*4，所以p = Ox100004
	return 0;
}





#include <stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//！！！注意数组元素
	//int a[3][2] = {1,3,5}
	//1 3 
	//5 0
	//0 0
	int* p;
	p = a[0];//a[0]为1的地址
	printf("%d", p[0]);//1
	return 0;
}






#include <stdio.h>
int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	//1 2 3 4 5
	//6 7 8 9 10
	int* ptr1 = (int*)(&aa + 1);//&aa是整个二维数组的地址，+1跳过这个二维数组，&aa+1指向数组后面
	//在强制类型转换成int*，然后赋值给ptr1，所以prt1也指向数组后面
	int* ptr2 = (int*)(*(aa + 1));//*（aa+1） —> aa[1]，表示二维数组第二行的数组名也是第二行第一个元素地址
	//在强制类型转换成int*，然后赋值给ptr2，所以prt1也指向第二行第一个元素地址
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10 5
	return 0;
}






假设环境是x86环境，程序输出的结果是啥？ 
#include <stdio.h>
int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;//都是数组指针类型，强行赋值，左边说的算
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	//指针相减表示指针之间元素的个数
	//%d默认有符号整型，所以&p[4][2]-&a[4][2]直接就是-4
	//%p取地址不能为负数，所以利用原码，补码和反码，我们计算都是用反码
	//所以-4
	//10000000000000000000000000000100  -4原码
	// 
	//11111111111111111111111111111011  -4反码

	//11111111111111111111111111111100  -4补码
	//因为地址一般用十六进制显示，所以每4个二进制转成一个十六进制
	//所以；Ox FF FF FF FC
	//所以地址是打印FFFFFFFC
	return 0;
}




#include <stdio.h>
int main()
{
	//char* pc = "abcdef";
	char* a[] = { "work","at","alibaba" };//就等于char* a[] = {"w","a","a"};
	//a数组已经放了“w”，“a”，“a“的地址
	char** pa = a;//a表示首元素的地址，即“w”地址的地址
	//pa—>"w"地址d地址
	pa++;
	//pa++那么它就指向了存放"a"地址的地址
	//*pa就指向了“a”的地址
	printf("%s\n", *pa);//at  
	return 0;
}




#include <stdio.h>
int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);//POINT
	printf("%s\n", *-- * ++cpp + 3);//ER
	//先++再*再--再*最后+3
	printf("%s\n", *cpp[-2] + 3);//ST
	printf("%s\n", cpp[-1][-1] + 1);//EW
	return 0;
}







